-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local log = require 'mc.logging'
local singleton = require 'mc.singleton'
local class = require 'mc.class'
local bios_enum = require 'domain.bios_firmware.defs'
local package_builder = require 'domain.bios_firmware.package.package_builder'
local package_snapshot = require 'domain.bios_firmware.package.package_snapshot'
local upgrade_state_machine = require 'service.upgrade_state_machine'
local fructl_handler = require 'infrastructure.fructl'
local skynet = require 'skynet'
local bios_factory = require 'factory.bios_factory'
local pfr_service = require 'service.pfr_service'
local base_messages = require 'messages.base'
local custom_messages = require 'messages.custom'
local imu_synchronize_bios = require 'domain.transport.imu_synchronize_bios'
local context = require 'mc.context'
local utils = require 'mc.utils'
local bs_util = require 'util.base_util'
local file_sec = require 'utils.file'
local obj_def = require 'macros.object_def'
local prop_def = require 'macros.property_def'
local spi_flash = require 'libmgmt_protocol.bios.infrastructure.spi_flash'
local task_mgmt = require 'mc.mdb.task_mgmt'
local client = require 'bios.client'
local ipmi = require 'ipmi'
local utils_core = require 'utils.core'
local clsmgmt = require 'mc.class_mgnt'
local comp_code = ipmi.types.Cc
local UpgradeService = class()
local spi_def = require 'macros.spi_def'
local bios_flash = require 'libmgmt_protocol.bios.domain.pfr.flash'

local MANUFACTUREID<const> = 0x0007db

local POWER_CYCLE<const> = 'PowerCycle'
local CHASSIS_COMMAND<const> = 'ChassisControlCommand'

local UPGRADE_PATH<const> = '/data/upgrade'
local CACHED_BIOS_PKG_PATH<const> = UPGRADE_PATH .. '/bios.tar.gz'

local CACHED_BIOS_REBOOT_AND_POWER_OFF_UPGRADE = 0x00 -- OS重启及下电均升级缓存的BIOS（默认）
local CACHED_BIOS_REBOOT_DONOT_UPGRADE = 0x01 -- 仅OS下电升级缓存的BIOS
local CACHED_BIOS_REBOOT_AND_POWER_OFF_DONT_UPGRADE = 0x02 -- OS重启及下电均不升级缓存的BIOS
local CACHED_BIOS_UPGRADE_MODE_MAX = 0x03

local ALLOWED_BIOS_SECURITY_UPGRADE<const> = 0 --仅允许安全包升级
local ALLOWED_BIOS_LOW_SECURITY_UPGRADE<const> = 1 --仅允许低安全包升级
local BIOS_PACKAGE_TYPE_SET<const> = 1
local BIOS_PACKAGE_TYPE_NOT_SET<const> = 0
local PACKAGE_TYPE_UNDEFINED<const> = 0xFF --未定义允许升级的包类型

local _, mtd_api = pcall(require, 'mtd.drv')
local task_state<const> = task_mgmt.state
local task_status<const> = task_mgmt.status

local REPLY_ERR<const> = -1
local REPLY_OK<const> = 0
local START_FINISH<const> = 254
local ALL_SYSTEM_ID<const> = 0xff

local Upgrade = class()

function Upgrade:ctor(state_machine, db)
    self.state_machine = state_machine
    self.db = db
    self.upgrade_mode = bios_enum.UpgradeMode.Cold
    self.cache_path = CACHED_BIOS_PKG_PATH
    self.origin_mode = bios_enum.UpgradeMode.Cold
    self.para = {}
    self.export_task = false
end

function Upgrade:_set_state_machine(event)
    self.state_machine:run(event, 'bios')
end

function Upgrade:upgrade_mode_recover()
    -- 热升级需要恢复成冷升级
    if self.upgrade_mode == bios_enum.UpgradeMode.Hot then
        self.upgrade_mode = bios_enum.UpgradeMode.Cold
    end
    -- 升级结束需要恢复为原升级模式
    self.upgrade_mode = self.origin_mode
    log:notice('[bios]mode recover to %s', bios_enum.UpgradeModeStr[self.upgrade_mode])
end

function Upgrade:_recover()
    self:_set_state_machine(bios_enum.UpgradeEvent.Error)
    if self.package then
        self.package:dtor()
        self.package = nil
    end
    self:upgrade_mode_recover()
end

function Upgrade:set_upgrade_mode(mode)
    -- 升级模式：强制模式、热升级、冷升级
    self.upgrade_mode = mode
end

function Upgrade:enable_force_mode()
    -- 装备模式下，强制升级需要恢复
    self.origin_mode = bios_enum.UpgradeMode.Force
    -- 升级模式：强制模式、热升级、冷升级
    self.upgrade_mode = bios_enum.UpgradeMode.Force
end

function Upgrade:construct_upgrade_info(upgrade_path)
    local upgrade_info = {
        db = self.db,
        upgrade_path = upgrade_path,
        upgrade_mode = self.upgrade_mode,
        hpm_delete_flag = true
    }
    return upgrade_info
end

-- 控制使能打开时，基准值和实际度量值不匹配会阻止业务侧系统正常启动。
-- bios升级之后需要更新对bios度量的基准值。
function Upgrade:update_tpcm_bios_measured_standard_digest()
    local obj = nil
    for _, config_obj in pairs(client:GetConfigObjects()) do
        obj = config_obj
    end
    if not obj then
        log:error("[TPCM]Unable to get tpcm config object or product not support!")
        return
    end
    local ctx = context.get_context() or context.new('N/A', 'N/A', '127.0.0.1')
    local ok, _ = obj.pcall:RefreshBIOSDigest_PACKED(ctx)
    if not ok then
        log:error("[TPCM]refresh bios digest after upgrade bios failed!")
        return
    end
    log:notice("[TPCM]refresh bios digest after upgrade bios success!")
end

function Upgrade:get_preserve_config_enabled(cfg)
    local snapshots = cfg.Snapshots
    if not snapshots or not next(snapshots) then
        log:error('[bios]upgrade finish reset bios: get snapshots failed')
        return true
    end
    for _, snapshot in pairs(snapshots) do
        if snapshot:get_preserve_config_enabled() ~= nil then
            return snapshot:get_preserve_config_enabled()
        end
    end
    return true
end

function Upgrade:is_need_reset_bios(preserve_config_enabled)
    if (next(self.para) ~= nil and self.para.RestoreFactorySettings and self.para.RestoreFactorySettings == 'true')
        or not preserve_config_enabled then
        log:notice('[bios] RestoreFactorySettings is %s, PreserveConfigEnabled is %s, need reset bios config',
            self.para.RestoreFactorySettings, preserve_config_enabled)
        return true
    end
    return false
end

local HpmUpgrade = class(Upgrade)

function HpmUpgrade:init()
    self.upgrade_mode = bios_enum.UpgradeMode.Cold
    self.post_state = {}
    self.sync_result = bios_enum.PostResult.Success
    self.upgrade_result = bios_enum.UpgradeResult.Success
end

function HpmUpgrade:init_post_state()
    self.post_state = {}
    self.sync_result = bios_enum.PostResult.Success
    self.upgrade_result = bios_enum.UpgradeResult.Success
end

local function get_the_bios_package_info(db)
    local bios_package_info = db:select(
        db.AllowedBiosUpgradeTable
    ):where(db.AllowedBiosUpgradeTable.Id:eq(1)):first()
    if not bios_package_info then
        -- 初次升级时， 此表没任何数据，需要插入一条初始数据
        db.AllowedBiosUpgradeTable({Id = 1, PackageStatus = BIOS_PACKAGE_TYPE_NOT_SET,
            AllowedPackageType = PACKAGE_TYPE_UNDEFINED}):save()
        bios_package_info = db:select(
            db.AllowedBiosUpgradeTable
        ):where(db.AllowedBiosUpgradeTable.Id:eq(1)):first()
    end
    return bios_package_info
end

function HpmUpgrade:_prepare(cfg)
    log:notice('[bios]upgrade prepare: build package')
    local system_id = cfg.system_id
    self.package = package_builder.new():build(cfg.cfg_path)
    local bios_package_info = get_the_bios_package_info(self.db)
    local allow_bios_upgrade = self.package:is_allowed_bios_upgrade(bios_package_info.AllowedPackageType)
    if not allow_bios_upgrade then
        error("[bios]package type is not matched, not allowed to upgrade")
    end
    log:notice('[bios]upgrade prepare: pfr cache tmp hpm')
    pfr_service.get_instance():cache_hpm(system_id, cfg.hpm_path)
    log:notice('[bios]upgrade prepare: reply messgae')
end

function HpmUpgrade:prepare(cfg)
    log:notice('[bios]upgrade prepare: start')
    self:_set_state_machine(bios_enum.UpgradeEvent.PrepareStart)
    local ok, err = pcall(function()
        self:_prepare(cfg)
        self:_set_state_machine(bios_enum.UpgradeEvent.PrepareFinish)
    end)
    if not ok then
        self:_recover()
        error(err)
    end
end

local function deal_activate_mode(activate_modes)
    if not activate_modes then
        return nil
    end
    local all_activate = false
    local none_activate = false
    for _, v in pairs(activate_modes) do
        if v == 'None' then
            none_activate = true
        end
        if v == 'All' then
            all_activate = true
        end
    end
    if all_activate then
        return {'All'}
    end
    if none_activate then
        return nil
    end
    return activate_modes
end

local function cal_bitmap(firmwar_id_list)
    if not firmwar_id_list then
        return nil
    end
    local _, name = next(firmwar_id_list)
    if name == 'All' then
        return 0xff
    end
    local bitmap = 0
    for _, component in pairs(firmwar_id_list) do
        local component_bitmap = component.BitMap
        bitmap = bitmap | (1 << component_bitmap)
    end
    return bitmap
end

-- 构造热升级参数
--                  升级列表             生效BitMap
-- ['All']:         全部组件             0xFF
-- ['None']:        全部组件             Nil
-- ['IMU','M7']:    IMU\M7的ID          IMU\M7的Bitmap
local ACTIVATE_ALL<const> = 0xFF
function HpmUpgrade:get_hot_upgrade_bitmap(components, firmware_id_list)
    if not components then
        return nil
    end
    local _, component = next(components)
    if component == 'All' then
        return ACTIVATE_ALL
    else
        return cal_bitmap(firmware_id_list)
    end
end

local function get_op_initiator()
    local initiator = {}
    local ctx = context.get_context()
    if ctx and ctx.has_initiator and ctx:has_initiator() then
        initiator = ctx:get_initiator()
    else
        local mc_initiator = require 'mc.initiator'
        initiator = mc_initiator.new('N/A', 'N/A', 'localhost')
    end

    return initiator
end

local SYSTEM_ID<const> = 1
function HpmUpgrade:hot_upgrade_info(cfg, system_id)
    local bios_ser = bios_factory.get_service('bios_service')
    local components = deal_activate_mode(cfg.ActivateComponents)
    -- 此处为无感升级，适配multihost
    local firmware_id_list = bios_ser:get_componet_bitmap_list(system_id, components)
    if not firmware_id_list or not next(firmware_id_list) then
        log:operation(get_op_initiator(), 'BIOS', 'BIOS hot upgrade failed')
        error('[bios]build hot upgrade info: firmware id list nil')
    end
    local upgrade_info = self:construct_upgrade_info(cfg.file_path)
    -- 构造升级的bitmap
    upgrade_info.activate_bitmap = self:get_hot_upgrade_bitmap(components, firmware_id_list)
    log:notice('[bios]activate bitmap(%s)', upgrade_info.activate_bitmap)
    upgrade_info.firmware_id_list = firmware_id_list
    log:notice('[bios]firmware id len(%s)', #firmware_id_list)
    return upgrade_info
end

-- 构造冷升级参数
function HpmUpgrade:cold_upgrade_info(cfg)
    return self:construct_upgrade_info(cfg.file_path)
end

-- 构造强制升级参数
function HpmUpgrade:force_upgrade_info(cfg)
    return self:construct_upgrade_info(cfg.file_path)
end

-- 构造现网包强制升级参数
function HpmUpgrade:online_force_upgrade_info(cfg, system_id)
    local power_status = fructl_handler.get_power_status(system_id)
    if power_status ~= 'OFF' then
        error('[bios]force_upgrade_info: current powerstate is on,not support force upgrade')
    end

    return self:construct_upgrade_info(cfg.file_path)
end

function HpmUpgrade:build_upgrade_mode(cfg, system_id)
    if self.para and self.para.ForceUpgrade == "force" then
        log:notice("build_upgrade_mode ForceUpgrade! system: %s", system_id)
        self.upgrade_mode = bios_enum.UpgradeMode.Force
        return
    end
    if cfg.is_online_force then
        self.upgrade_mode = bios_enum.UpgradeMode.OnlineForce
        return
    end
    -- 强制升级模式，并且定制为下次重启，需要将启动模式改为Cold
    if cfg.action and tonumber(cfg.action) == bios_enum.FirmwareEffectiveActions.WaitReset and
        self.upgrade_mode == bios_enum.UpgradeMode.Force then
        log:notice('[bios]change upgrade mode from force to cold')
        self.upgrade_mode = bios_enum.UpgradeMode.Cold
        return
    end
    if self.upgrade_mode == bios_enum.UpgradeMode.Force then
        return
    end
    if cfg.ActivateComponents then
        self.upgrade_mode = bios_enum.UpgradeMode.Hot
    else
        self.upgrade_mode = bios_enum.UpgradeMode.Cold
    end
    if self.upgrade_mode == bios_enum.UpgradeMode.Hot then
        local bios_ser = bios_factory.get_service('bios_service')
        if bios_ser:get_prop('SystemStartupState', system_id) ~= START_FINISH then
            log:operation(get_op_initiator(), 'BIOS', 'BIOS hot upgrade failed')
            error('[bios]build upgrade mode: current state not support hot upgrade')
        end
    end
end

function HpmUpgrade:build_upgrade_info(cfg, system_id)
    local upgrade_info_fun = {
        [bios_enum.UpgradeMode.Hot] = HpmUpgrade.hot_upgrade_info,
        [bios_enum.UpgradeMode.Cold] = HpmUpgrade.cold_upgrade_info,
        [bios_enum.UpgradeMode.Force] = HpmUpgrade.force_upgrade_info,
        [bios_enum.UpgradeMode.OnlineForce] = HpmUpgrade.online_force_upgrade_info
    }
    return upgrade_info_fun[self.upgrade_mode](self, cfg, system_id)
end

function HpmUpgrade:check_patch_version(system_id)
    local bios_ser = bios_factory.get_service('bios_service')
    self.package:prepare({
        Version = bios_ser:get_bios_prop('Version', system_id)
    })
end

function HpmUpgrade:build_info(cfg, system_id)
    self:build_upgrade_mode(cfg, system_id)
    log:notice('[bios]upgrade process: system %s, upgrade mode(%s), period(%s), package type(%s)',
        system_id, bios_enum.UpgradeModeStr[self.upgrade_mode],
        bios_enum.PackagePeriodStr[self.package.period],
        bios_enum.PackageTypeStr[self.package.package_type])
    local upgrade_info = self:build_upgrade_info(cfg, system_id)
    upgrade_info.system_id = system_id
    upgrade_info.spi_rate = cfg.spi_rate
    upgrade_info.multihost = cfg.Multihost
    upgrade_info.hpm_delete_flag = true
    return upgrade_info
end

function HpmUpgrade:multihost_upgrade(fru_state, startup_state, system_id, upgrade_info, snapshot)
    local bios_ser = bios_factory.get_service('bios_service')
    -- 装备模式或下电状态直接升级主片
    if self.upgrade_mode == bios_enum.UpgradeMode.Force or fru_state == 'OFF' or not startup_state then
        bios_ser:set_spi_channel_direction(system_id)
        self.package:process(upgrade_info, snapshot)
    elseif startup_state then  -- 支持主备同步，上电升级从片，同步主片
        if self.upgrade_mode == bios_enum.UpgradeMode.Hot then
            self.package:process(upgrade_info, snapshot)
            local pfr_ser = pfr_service.get_instance()
            pfr_ser:cache_hpm_after_activate(snapshot)
        else
            bios_ser:set_spi_channel_direction(system_id, true)
            self.package:process(upgrade_info, snapshot)
            self:post_synchronize_and_verify(system_id)
        end
    end
end

function HpmUpgrade:multiflash_upgrade(upgrade_info, snapshot, system_id, flash_channel_ids)
    local bios_ser = bios_factory.get_service('bios_service')
    local flash_num = bios_ser:get_prop(obj_def.FLASH_NUM, system_id)
    for i = 1, flash_num, 1 do
        local channel_id = flash_channel_ids[i] or i - 1
        bios_ser:set_prop(obj_def.FLASH_CHANNEL, channel_id, system_id)
        pcall(function()
            spi_flash.rmmod_driver(spi_def.SPIKODRV, prop_def.DEVNAME_BIOS_V6, client, client.PProxyDriverRmmod)
        end)
        self.package:process(upgrade_info, snapshot)
    end
end

function HpmUpgrade:_process_multihost(snapshot, cfg)
    local bios_ser = bios_factory.get_service('bios_service')
    local system_id = snapshot:get_system_id()
    local upgrade_info = self:build_info(cfg, system_id)
    local startup_state = bios_ser:get_prop('SystemStartupState', system_id) == START_FINISH
    local flash_channel_ids = bios_ser:get_prop(obj_def.FLASH_CHANNEL_IDS, system_id) or {}
    upgrade_info.startup_finished = startup_state
    local fru_state = fructl_handler.get_power_status(system_id)
    log:notice('[bios] multihost upgrade process: system id: %s, upgrade mode: %s, power state: %s, startup state: %s',
        system_id, self.upgrade_mode, fru_state, startup_state)
    self:check_patch_version(system_id)

    if not cfg.Multihost or #flash_channel_ids ~= 0 then
        self:multiflash_upgrade(upgrade_info, snapshot, system_id, flash_channel_ids)
        --升级完成后通道切回
        bios_ser:set_prop(obj_def.FLASH_CHANNEL, 0, system_id)
    else
        self:multihost_upgrade(fru_state, startup_state, system_id, upgrade_info, snapshot)
    end

end

function HpmUpgrade:cache_version(cfg)
    self.cached_version = {}
    local bios_ser = bios_factory.get_service('bios_service')
    for _, snapshot in pairs(cfg.Snapshots) do
        local system_id = snapshot:get_system_id()
        local bios_version = bios_ser:get_bios_prop('Version', system_id)
        self.cached_version[system_id] = bios_version
    end
end

function HpmUpgrade:get_cached_version(system_id)
    return self.cached_version and self.cached_version[system_id]
end

-- 构造升级参数
function HpmUpgrade:_process(cfg)
    self:cache_version(cfg)
    local bios_ser = bios_factory.get_service('bios_service')
    for _, snapshot in pairs(cfg.Snapshots) do
        local system_id = snapshot:get_system_id()
        local flash_num = bios_ser:get_prop(obj_def.FLASH_NUM, system_id)
        if cfg.Multihost or flash_num > 1 then
            local ok, res = pcall(self._process_multihost, self, snapshot, cfg)
            if not ok then
                self.post_state[snapshot:get_system_id()] = bios_enum.UpgradeState.UpgradeFailed
                log:error('[bios(system id:%s)]upgrade fail: %s', snapshot:get_system_id(), res)
            else
                log:notice('[bios(system id:%s)]upgrade success', snapshot:get_system_id())
            end
        else
            log:notice('[bios] singlehost upgrade process')
            local upgrade_info = self:build_info(cfg, bios_enum.SINGLE_HOST_SYSTEM_ID)
            self:check_patch_version(bios_enum.SINGLE_HOST_SYSTEM_ID)
            self.package:process(upgrade_info, snapshot)
            local pfr_ser = pfr_service.get_instance()
            pfr_ser:cache_hpm_after_activate(snapshot)
        end
    end
    self:_verify_sync(cfg)
end

function HpmUpgrade:_verify_sync(cfg)
    for _, snapshot in pairs(cfg.Snapshots) do
        local system_id = snapshot:get_system_id()
        local res = self:handle_upgrade_state(snapshot, system_id, 0)
        local msg_head = cfg.Multihost and string.format('[SystemId: %s]', system_id) or ''
        local pre_bios_ver = self:get_cached_version(system_id) or ''
        if not res then
            log:error('[bios]Upgrade Bios failed, system id: %s', system_id)
            log:running(log.RLOG_ERROR, msg_head .. string.format('Upgrade Bios%s Firmware(From: %s) failed',
                cfg.Multihost and system_id or '', pre_bios_ver))
        else
            log:running(log.RLOG_INFO, msg_head .. string.format('Upgrade Bios%s Firmware(From: %s) successfully',
                cfg.Multihost and system_id or '', pre_bios_ver))
        end
    end

    if self.upgrade_result == bios_enum.UpgradeResult.Failed then
        error('Upgrade bios failed')
    elseif self.sync_result == bios_enum.PostResult.Failed then
        error('Post synchronize action and verify failed')
    end
end

local function retry_synchronize(system_id)
    local imu_cmd = imu_synchronize_bios.get_instance()
    imu_cmd:post_synchronize(system_id)
    skynet.sleep(1000)  -- 等待10s
    return imu_cmd:check_post_synchronize_finished(system_id)
end

-- 发送同步命令并校验从区bios同步状态
function HpmUpgrade:post_synchronize_and_verify(system_id)
    self.post_state[system_id] = bios_enum.UpgradeState.SyncProcess
    local imu_cmd = imu_synchronize_bios.get_instance()
    imu_cmd:post_synchronize(system_id)
    skynet.fork(function()
        local res = imu_cmd:check_post_synchronize_finished(system_id)
        if not res and not retry_synchronize(system_id) then
            self.post_state[system_id] = bios_enum.UpgradeState.SyncFailed
            log:error('[bios] post synchronize action and retry failed, system id: %s', system_id)
        else
            self.post_state[system_id] = bios_enum.UpgradeState.Success
        end
    end)
end

function HpmUpgrade:handle_upgrade_state(snapshot, system_id, retry_times)
    if not self.post_state[system_id] then
        return true
    end

    if self.post_state[system_id] == bios_enum.UpgradeState.UpgradeFailed then  -- 升级任务失败
        self.upgrade_result = bios_enum.UpgradeResult.Failed
        return false
    elseif self.post_state[system_id] == bios_enum.UpgradeState.SyncFailed then  -- 主备同步任务失败
        self.sync_result = bios_enum.PostResult.Failed
        return false
    elseif self.post_state[system_id] == bios_enum.UpgradeState.SyncProcess then  -- 主备同步中
        if retry_times > 10 then
            self.sync_result = bios_enum.PostResult.Failed
            return false
        end

        for _ = 1, 10 do
            skynet.sleep(600)  --sleep 6s
            if self.post_state[system_id] ~= bios_enum.UpgradeState.SyncProcess then
                break
            end
        end
        return self:handle_upgrade_state(snapshot, system_id, retry_times + 1)
    end

    -- 主备升级
    snapshot:set_slave_upgrade(true)
    log:notice('[bios]system %s set slave upgrade true', system_id)
    return true
end

function HpmUpgrade:process(cfg)
    log:notice('[bios]upgrade process: start')
    self:_set_state_machine(bios_enum.UpgradeEvent.ProcessStart)
    local ok, err = pcall(function()
        self:_process(cfg)
        self:init_post_state()
        self:_set_state_machine(bios_enum.UpgradeEvent.ProcessFinish)
    end)
    self.para.ForceUpgrade = nil
    if not ok then
        self:_recover()
        self:init_post_state()
        error(err)
    end
end

function HpmUpgrade:set_firmware_status_after_finish(snapshots)
    if not snapshots then
        log:error('[bios]snapshots is nil')
        return
    end
    for system_id, snapshot in pairs(snapshots) do
        -- 判断是否有缓存包
        if snapshot:is_cache() then
            snapshot:set_firmware_effective_status(bios_enum.FirmwareEffectiveStatus.UnEffective) --（待生效）固件待写入flash
            log:notice('[bios]FirmwareEffectiveStatus change to UnEffective, system id : %s', system_id)
        else
            snapshot:set_firmware_effective_status(bios_enum.FirmwareEffectiveStatus.Effecting) -- 生效中
            log:notice('[bios]FirmwareEffectiveStatus change to Effecting, system id : %s', system_id)
        end
    end
end

function HpmUpgrade:multihost_reset_bios(cfg)
    local bios_ser = bios_factory.get_service('bios_service')
    for _, snapshot in pairs(cfg.Snapshots) do
        local power_status = fructl_handler.get_power_status(snapshot.SystemId)
        if power_status == 'OFF' then
            local ctx = context.get_context()
            bios_ser:reset_bios(ctx, snapshot.SystemId)
            log:notice('[bios]hpm upgrade: reset bios(system %s) setting successfully', snapshot.SystemId)
        end
    end
end

function HpmUpgrade:finish(cfg)
    log:notice('[bios]upgrade finish: start')
    self:_set_state_machine(bios_enum.UpgradeEvent.FimwareStart)
    self:_set_state_machine(bios_enum.UpgradeEvent.FimwareFinish)
    self:upgrade_mode_recover()
    log:notice('[bios]hpm upgrade: bios package upgrade successfully')
    skynet.fork_once(function()
        self:update_tpcm_bios_measured_standard_digest()
        local preserve_config_enabled = self:get_preserve_config_enabled(cfg)
        if self:is_need_reset_bios(preserve_config_enabled) then
            self:multihost_reset_bios(cfg)
            self.para = {}
        end

        self:set_firmware_status_after_finish(cfg.Snapshots)
        local pfr_ser = bios_factory.get_service('pfr_service')
        for _, snapshot in pairs(cfg.Snapshots) do
            pfr_ser:try_unlock_forever(snapshot.SystemId)
        end
    end)
end

function HpmUpgrade:upgrade(phrase, cfg, snapshots)
    local fun_map = {
        prepare = HpmUpgrade.prepare,
        process = HpmUpgrade.process,
        finish = HpmUpgrade.finish
    }
    local fun = fun_map[phrase]
    if not fun then
        log:error('[bios]hpm upgrade: phrase(%s) invalid', phrase)
        return
    end
    cfg.Snapshots = snapshots
    fun(self, cfg)
end

local EffectiveUpgrade = class(Upgrade)

function EffectiveUpgrade:init()
    self.origin_mode = bios_enum.UpgradeMode.PowerOffEffective
    self.upgrade_mode = bios_enum.UpgradeMode.PowerOffEffective
end

-- 下电：缓存升级
-- 重启：下电，再缓存升级
function EffectiveUpgrade:_effective_prepare(snapshot)
    log:notice('[bios]effective upgrade: start prepare upgrade bios package')
    self:_set_state_machine(bios_enum.UpgradeEvent.PrepareStart)
    local ok, err = pcall(function()
        self.package = package_builder.new():build_with_snapshot(snapshot)
        self:_set_state_machine(bios_enum.UpgradeEvent.PrepareFinish)
    end)
    if not ok then
        self:_recover()
        error(err)
    end
end

function EffectiveUpgrade:check_multiflash_and_effective_process(upgrade_info, snapshot)
    local system_id = snapshot:get_system_id()
    local bios_ser = bios_factory.get_service('bios_service')
    local flash_num = bios_ser:get_prop(obj_def.FLASH_NUM, system_id)
    local flash_channel_ids = bios_ser:get_prop(obj_def.FLASH_CHANNEL_IDS, system_id) or {}
    if flash_num > 1 or #flash_channel_ids ~= 0 then
        upgrade_info.hpm_delete_flag = false
        for i = 1, flash_num, 1 do
            local channel_id = flash_channel_ids[i] or i - 1
            bios_ser:set_prop(obj_def.FLASH_CHANNEL, channel_id, system_id)
            pcall(function ()
                spi_flash.rmmod_driver(spi_def.SPIKODRV, prop_def.DEVNAME_BIOS_V6, client, client.PProxyDriverRmmod)
            end)
            if i == flash_num then
                upgrade_info.hpm_delete_flag = true
            end
            self.package:process(upgrade_info, snapshot)
        end
    else
        self.package:process(upgrade_info, snapshot)
    end
end

function EffectiveUpgrade:_effective_process(snapshot, spi_rate)
    log:notice('[bios]effective upgrade: start process upgrade bios package')
    self:_set_state_machine(bios_enum.UpgradeEvent.ProcessStart)
    local ok, err = pcall(function()
        -- 强制升级模式，并且定制为下次重启，需要将启动模式改为Cold
        if self.upgrade_mode == bios_enum.UpgradeMode.Force then
            log:notice('[bios]effective process:change upgrade mode from force to cold')
            self.upgrade_mode = bios_enum.UpgradeMode.Cold
        end
        self.cache_path = bs_util.get_bios_cached_path(snapshot.SystemId)
        local bios_ser = bios_factory.get_service('bios_service')
        bios_ser:set_spi_channel_direction(snapshot.SystemId)
        local upgrade_info = self:construct_upgrade_info(self.cache_path)
        upgrade_info.spi_rate = spi_rate
        self:check_multiflash_and_effective_process(upgrade_info, snapshot)
        self:_set_state_machine(bios_enum.UpgradeEvent.ProcessFinish)
    end)
    if not ok then
        self:_recover()
        error(err)
    end
end

function UpgradeService:inner_upgrade(upgrade_path)
    local ok, err = pcall(function()
        local snapshot = self:get_package_snapshot(SYSTEM_ID)
        local package = package_builder.new():build_with_snapshot(snapshot)
        local package_type = package.package_type
        package.package_type = bios_enum.PackageType.Normal
        local upgrade_info = {
            db = self.db,
            upgrade_path = upgrade_path .. 'Firmware1',
            upgrade_mode = self.upgrade_mode,
            hpm_delete_flag = true
        }
        local ok, rsp = client:PFileFileChown(context.new(), nil, upgrade_info.upgrade_path, 104, 104)
        if not ok then
            log:error('[bios] chown upgrade file failed, error %s', rsp)
        end
        local is_recover, res = pcall(package.process, package, upgrade_info, snapshot)
        package.package_type = package_type
        if not is_recover then
            error(res)
        end
    end)
    if not ok then
        log:error('[bios]upgrade inner fail, error is %s', err)
    else
        log:notice('[bios]upgrade inner success')
    end
end

function EffectiveUpgrade:_effective_finish(snapshot)
    log:notice('[bios]effective upgrade: start finish upgrade bios package')
    self:_set_state_machine(bios_enum.UpgradeEvent.FimwareStart)
    self:_set_state_machine(bios_enum.UpgradeEvent.FimwareFinish)
    skynet.fork_once(function()
        self:update_tpcm_bios_measured_standard_digest()
        local preserve_config_enabled = snapshot:get_preserve_config_enabled()
        if self:is_need_reset_bios(preserve_config_enabled) then
            local ctx = context.get_context()
            local bios_ser = bios_factory.get_service('bios_service')
            bios_ser:reset_bios(ctx, snapshot.SystemId)
            log:notice('[bios]hpm upgrade: reset bios(system %s) setting successfully', snapshot.SystemId)
            self.para = {}
        end

        snapshot:set_firmware_effective_status(bios_enum.FirmwareEffectiveStatus.Effecting) -- 生效中
        log:notice('[bios]FirmwareEffectiveStatus change to UnEffective')

        local pfr_ser = bios_factory.get_service('pfr_service')
        pfr_ser:try_unlock_forever(snapshot.SystemId)
    end)
end

function EffectiveUpgrade:_power_off_effective(snapshot, spi_rate)
    if not snapshot or not snapshot:is_cache() then
        log:notice('[bios]effective upgrade no need activate bios, snapshot is %s', snapshot)
        return
    end

    log:notice('[bios]effective upgrade: start upgrade bios package, system id: %s', snapshot.SystemId)
    local ok, err = pcall(function()
        self:_effective_prepare(snapshot)
        self:_effective_process(snapshot, spi_rate)
        self:_effective_finish(snapshot)
    end)
    self:upgrade_mode_recover()
    if not ok then
        log:error('[bios]effective upgrade: upgrade bios package fail, %s', err)
        return
    end
    log:notice('[bios]effective upgrade: upgrade bios package successfuly')
end

local function need_power_cycle(snapshot, multihost)
    if not snapshot then
        return false
    end
    -- HPC：主备升级过，需要powercycle
    if multihost then
        if snapshot:get_slave_upgrade() then
            log:notice('[bios]system(%s) need powercycle, cause slave upgrade', snapshot:get_system_id())
            return true
        else
            log:notice('[bios]system(%s) not need powercycle, cause not slave upgrade', snapshot:get_system_id())
            return false
        end
    end
    -- 非HPC：没缓存包，不需要powercycle
    if not snapshot:is_cache() then
        log:notice('[bios]power cycle effective upgrade no need activate bios, snapshot is %s', snapshot)
        return false
    end
    return true
end

function EffectiveUpgrade:_power_cycle_effective(snapshot, multihost)
    if not need_power_cycle(snapshot, multihost) then
        return
    end

    local system_id = snapshot:get_system_id()
    local ok, _ = pcall(function()
        fructl_handler.set_power_state(POWER_CYCLE, CHASSIS_COMMAND, system_id)
    end)

    snapshot:set_slave_upgrade(false)
    if not ok then
        log:error('[bios]effective upgrade: system (%s) set power cycle fail', system_id)
        return
    end
    log:notice('[bios]effective upgrade: system (%s) set power cycle success', system_id)
end

function EffectiveUpgrade:set_mode_with_strategy()
    local strategy = fructl_handler.get_power_strategy()
    if strategy == 'AlwaysPowerOff' and self.upgrade_mode == bios_enum.UpgradeMode.Force then
        self.upgrade_mode = bios_enum.UpgradeMode.OnlineForce
        log:notice('[bios] set online force mode')
    end
end

local RepairUpgrade = class(EffectiveUpgrade)

local function wait_fructl_startup()
    local times = 100
    while times > 0 do
        local fructl_obj = fructl_handler.get_fructl_obj()
        if fructl_obj then
            return true
        end
        skynet.sleep(50)
        times = times - 1
    end
    return true
end

function RepairUpgrade:repair()
    local pkg_snapshot = package_snapshot.get_instance()
    if pkg_snapshot:is_complete() then
        log:notice('[bios]repair interrupt: package is complete, no need to repair')
        return
    end
    if not wait_fructl_startup() then
        log:error('[bios]repair interrupt: wait fructl timeout')
        return
    end
    log:notice('[bios]repair interrupt: start to repair')
    fructl_handler.set_power_state(POWER_CYCLE, CHASSIS_COMMAND)
end

function UpgradeService:ctor(db, bus)
    local state_machine = upgrade_state_machine.get_instance()
    self.state_machine_instance = state_machine
    self.hpm_upgrade = HpmUpgrade.new(state_machine, db)
    self.effective_upgrade = EffectiveUpgrade.new(state_machine, db)
    self.repair_upgrade = RepairUpgrade.new(state_machine, db)
    self.bus = bus
    self.db = db
    self.package_snapshot_collection = {}
    self.multihost = false
    self.upgrade_mode = bios_enum.UpgradeMode.Cold
end

function UpgradeService:delete_snapshot(system_id)
    local snapshot = self:get_package_snapshot(system_id)
    if not snapshot then
        return
    end
    clsmgmt('BiosUpgradeService'):remove(snapshot.obj)
    self.package_snapshot_collection[system_id] = nil
end

function UpgradeService:add_snapshot(system_id, obj)
    if self.package_snapshot_collection[system_id] then
        return
    end
    self.package_snapshot_collection[system_id] =
        package_snapshot.new(self.db, obj, system_id)
end

function UpgradeService:get_package_type(system_id)
    local pkt_snap = self.package_snapshot_collection[system_id]
    if not pkt_snap then
        return
    end
    return pkt_snap:get_package_type()
end

function UpgradeService:get_package_snapshot(system_id)
    return self.package_snapshot_collection[system_id]
end

function UpgradeService:get_package_snapshot_table(system_id)
    if not system_id then
        error('[bios]get package snapshot table failed, system id is nil')
    end
    if system_id == bios_enum.ALL_HOST_SYSTEM_ID then
        return self.package_snapshot_collection
    end
    return {[system_id] = self:get_package_snapshot(system_id)}
end

function UpgradeService:set_dft_mode()
    self.hpm_upgrade:enable_force_mode()
    self.effective_upgrade:enable_force_mode()
    self.repair_upgrade:enable_force_mode()
    self.upgrade_mode = bios_enum.UpgradeMode.Force
end

function UpgradeService:upgrade_hpm(phrase, cfg)
    local snapshots = self:get_package_snapshot_table(cfg.system_id)
    if not snapshots or not next(snapshots) then
        error(string.format('[bios]upgrade hpm: get snapshot fail, system id is %s', cfg.system_id))
    end
    cfg.Multihost = self.multihost
    cfg.spi_rate = self:get_spi_rate()
    self.hpm_upgrade:upgrade(phrase, cfg, snapshots)
    if phrase == 'prepare' then
        self.hpm_upgrade.para = cfg.para
        self.effective_upgrade.para = cfg.para
    end
end

function UpgradeService:power_cycle_effective(system_id)
    -- 只有单host有生效
    self.effective_upgrade:_power_cycle_effective(self:get_package_snapshot(system_id), self.multihost)
end

function UpgradeService:clear_slave_upgrade(system_id)
    local snapshot = self:get_package_snapshot(system_id)
    if not snapshot then
        return
    end
    snapshot:set_slave_upgrade(false)
    log:notice('[bios]clear system %s slave upgrade flag', system_id)
end

function UpgradeService:power_off_effective(system_id)
    if system_id == ALL_SYSTEM_ID then
        for _, snapshot in pairs(self.package_snapshot_collection) do
            self.effective_upgrade:_power_off_effective(snapshot, self:get_spi_rate())
        end
    else
        self.effective_upgrade:_power_off_effective(self:get_package_snapshot(system_id), self:get_spi_rate())
    end
end

local function get_active_condition(db)
    local record = db:select(
        db.CachedBiosUpgradeTable
    ):where(db.CachedBiosUpgradeTable.Id:eq(1)):first()

    return record and record.BiosActiveCondition or 'PowerOff'
end

function UpgradeService:register_multihost(param, pkg_snapshot)
    if pkg_snapshot and pkg_snapshot:is_cache() then
        param[3] = {Key = 'ActiveCondition', Value = 'ChassisPowerOff'}
        self:active_register(param)
        return
    end
end

function UpgradeService:judge_active_register(system_id)
    log:notice('[BIOS]Start judge power status')
    local param = bios_enum.BiosActiveRegisterList
    if self.multihost then
        if system_id == bios_enum.ALL_HOST_SYSTEM_ID then  -- all host
            for _, pkg_snapshot in pairs(self.package_snapshot_collection) do
                self:register_multihost(param, pkg_snapshot)
            end
        else
            local pkg_snapshot = self:get_package_snapshot(system_id)
            self:register_multihost(param, pkg_snapshot)
        end
    else
        local pkg_snapshot = self:get_package_snapshot(bios_enum.SINGLE_HOST_SYSTEM_ID)
        if pkg_snapshot and pkg_snapshot:is_cache() then
            param[3] = {Key = 'ActiveCondition', Value = get_active_condition(self.db)}
            self:active_register(param)
        end
    end
end

function UpgradeService:active_register(param)
    log:notice('[BIOS]Start judge power status, not OFF , start regitser')
    -- 只有bios生效模式不为OS重启及下电均不升级缓存的BIOS时，才向固件管理注册
    if self:get_cached_bios_upgrade_mode() < CACHED_BIOS_REBOOT_AND_POWER_OFF_DONT_UPGRADE then
        client:FirmwareActiveFirmwareActiveRegisterActiveAction(context.new(), param)
    end
end

function UpgradeService:set_power_cycle_after_finish(cfg)
    if self.hpm_upgrade.upgrade_mode ~= bios_enum.UpgradeMode.Force and cfg.action and
        tonumber(cfg.action) == bios_enum.FirmwareEffectiveActions.ImmediatelyReset then
        -- 重启
        log:notice('[bios]set_power_cycle_after_finish set_power_state')
        fructl_handler.set_power_state(POWER_CYCLE, CHASSIS_COMMAND, cfg.system_id)
    end
end

local COMP_CODE_FW_UPDATING<const> = 0xD1
local COMP_CODE_STATUS_INVALID<const> = 0xD5
local STATUS_INVALID<const> = -1

local function deal_activate_err(code)
    local err_map = {
        [COMP_CODE_FW_UPDATING] = function()
            error(custom_messages.BiosStateNotAllowed())
        end,
        [COMP_CODE_STATUS_INVALID] = function()
            error(custom_messages.ActionFailedByBiosUpgrade('activate BIOS'))
        end,
        [STATUS_INVALID] = function()
            error(base_messages.ActionNotSupported('activate BIOS'))
        end
    }
    if err_map[code] then
        err_map[code]()
    end
    log:error('[bios]deal activate_err: code(%s)', code)
    error(base_messages.ActionNotSupported('activate BIOS'))
end

local function gen_log_components_name(components_names)
    local len = #components_names
    local log_str = ''
    local index = 1
    for _, component in pairs(components_names) do
        local components_name = component.Name
        if index == 1 then
            log_str = log_str .. components_name 
        elseif index == len then
            log_str = log_str .. ' and ' .. components_name
        else
            log_str = log_str .. ', ' .. components_name
        end
        index = index + 1
    end
    return log_str
end

function UpgradeService:checkout_before_activate(ctx, system_id)
    local bios_ser = bios_factory.get_service('bios_service')
    if not bios_ser or bios_ser:get_prop('SystemStartupState', system_id) ~= START_FINISH then
        log:error('[bios]activate components: system %s curent state not support', system_id)
        bs_util.record_operation(ctx, system_id,
            'BIOS hot upgrade fails due to abnormal BIOS status')
        error(custom_messages.BiosStateNotAllowed())
    end

    if self.hpm_upgrade.upgrade_mode == bios_enum.UpgradeMode.Hot then
        log:error('[bios]activate components: upgrading not support activate')
        bs_util.record_operation(ctx, system_id, 'Activate BIOS failed')
        -- 修改错误类型,ActionFailedByBiosUpgrade
        error(custom_messages.ActionFailedByBiosUpgrade('activate BIOS'))
    end
end

function UpgradeService:activate_components(ctx, components_names)
    local bios_ser = bios_factory.get_service('bios_service')
    if bios_ser and bios_ser:is_multihost() then
        self:activate_multihost_components(ctx, components_names)
    else
        self:activate_single_component(ctx, components_names)
    end
end

function UpgradeService:activate_multihost_components(ctx, components_names)
    if self.activate_status then
        return
    end
    self.activate_status = true
    skynet.fork(function()
        local snapshots = self:get_package_snapshot_table(bios_enum.ALL_HOST_SYSTEM_ID)
        if not snapshots then
            self.activate_status = false
            return
        end
        local ok, err
        for _, snapshot in pairs(snapshots) do
            ok, err = pcall(UpgradeService.activate_component,
                self, ctx, components_names, snapshot)
            if not ok then
                log:error('[bios]system %s activate fail %s', snapshot:get_system_id(), err)
            end
        end
        self.activate_status = false
    end)
end

function UpgradeService:activate_single_component(ctx, components_names)
    return self:activate_component(ctx,
        components_names, self:get_package_snapshot(bios_enum.SINGLE_HOST_SYSTEM_ID))
end

function UpgradeService:activate_component(ctx, components_names, snapshot)
    if not snapshot then
        return
    end
    if not components_names or #components_names == 0 then
        log:error('[bios]activate components: componets name is empty')
        error(custom_messages.PropertyValueNotEmpty())
    end
    local system_id = snapshot:get_system_id()
    self:checkout_before_activate(ctx, system_id)
    local activate_modes = deal_activate_mode(components_names)
    if not activate_modes then
        log:notice('[bios]activate components: system %s no need activate componets name', system_id)
        return
    end
    local ok, components_res = pcall(function()
        local bios_ser = bios_factory.get_service('bios_service')
        return bios_ser:get_componet_bitmap_list(system_id, activate_modes)
    end)
    if not ok then
        bs_util.record_operation(ctx, system_id, 'Activate BIOS failed')
        log:error('[bios]activate components: system %s get componets name bitmap fail', system_id)
        error(components_res.Code or base_messages.InternalError())
    end
    local bitmap = cal_bitmap(components_res)
    local ok, res = pcall(snapshot.activate, snapshot, bitmap)
    if not ok then
        bs_util.record_operation(ctx, system_id, string.format('Activate %s failed',
            gen_log_components_name(components_res)))
        deal_activate_err(res.Code)
    end
    local pfr_ser = pfr_service.get_instance()
    pfr_ser:cache_hpm_after_activate(snapshot)
    bs_util.record_operation(ctx, system_id, string.format('Activate %s successfully',
        gen_log_components_name(components_res)))
end

function UpgradeService:set_collection(sys_id)
    log:notice('export bios(%s) firmware', sys_id)
    local bios_ser = bios_factory.get_service('bios_service')
    self.bios_object_collection = {}
    if not sys_id then
        for k, _ in pairs(bios_ser.bios_object_collection) do
            self.bios_object_collection[k] = bios_ser.bios_object_collection[k]
        end
    else
        for k, _ in pairs(bios_ser.bios_object_collection) do
            if k == sys_id then
                self.bios_object_collection[k] = bios_ser.bios_object_collection[k]
            end
        end
    end
end

function UpgradeService:export_bios_firmware(ctx, path)
    if self.export_task then
        log:error('Already exists an export bios firmware task')
        error(custom_messages.BiosFirmwareExportingErr())
    end
    local _, _, task_id = task_mgmt.create_task(self.bus, 'Download Bios', path)
    self.export_task = task_id
    skynet.fork(function()
        log:notice('start to export bios firmware')
        -- 防止异常情况导致程序崩溃，EXPORT_TASK未恢复为nil，导致固件导出功能不可用
        local ok, ret = pcall(function ()
            return self:download_multi_bios(task_id, ctx)
        end)
        log:notice('export bios firmware finished')
        if ok then
            log:operation(ctx:get_initiator(), 'BIOS', "Download BIOS Successfully")
        else
            log:error('download_multi_bios failed, ret = %s', ret)
            log:operation(ctx:get_initiator(), 'BIOS', "Download BIOS failed")
        end
        self.export_task = nil
    end)
    return task_id
end

local function error_msg()
    local result = ''
    return function (sysid)
        if not sysid then
            return result
        end
        if #result == 0 then
            result = tostring(sysid)
        else
            result = string.format('%s, %s', result, sysid)
        end
    end
end

local function generate_all_sys_id(bios_collection)
    if not bios_collection or type(bios_collection) ~= 'table' or
        bs_util.get_tbl_len(bios_collection) == 0 then
        return 'None'
    end
    local system_id_str = ''
    for sysid, _ in pairs(bios_collection) do
        if #system_id_str == 0 then
            system_id_str = tostring(sysid)
        else
            system_id_str = string.format('%s, %s', system_id_str, sysid)
        end
    end
    return system_id_str
end

-- 下载BIOS固件之前的准备工作
local function pre_download_action(self, task_id)
    log:notice('=========pre download action=========')
    local size = bs_util.get_tbl_len(self.bios_object_collection)
    if size == 0 then
        task_mgmt.update_task(task_id, {State = task_state.Exception, Status = task_status.Error,
            MessageId = 'BiosFirmwareExportFailed',
            MessageArgs = 'None'})
        error('NO BIOS firmware needs to be exported')
    else
        log:notice('Expected number of BIOS firmware to be exported is %s', size)
    end
    log:notice('start to create export bios path')
    local ret = utils.mkdir_with_parents(prop_def.EXPORT_BIOS_PATH,
        utils.S_IRWXU | utils.S_IRGRP | utils.S_IXGRP)
    if ret ~= 0 then
        task_mgmt.update_task(task_id, {State = task_state.Exception, Status = task_status.Error,
            MessageId = 'BiosFirmwareExportFailed',
            MessageArgs = {generate_all_sys_id(self.bios_object_collection)}})
        error(string.format('create export bios path failed, ret = %s', ret))
    end
    task_mgmt.update_task(task_id, {State = task_state.Running,
        Progress = 5, Status = task_status.OK})
    log:notice('=========pre download action finished=========')
end

-- 下载单个bios固件
local function download_one_bios(self, sysid, task_id, process_bar_start_point, remain_process_bar)
    local export_path = prop_def.EXPORT_BIOS_PATH .. 'bios' .. sysid .. '.bin'
    local frequency_mhz, firmware_size, is_support = self:check_soc_type(sysid)
    if not is_support then
        log:error('check_soc_type failed, frequency_mhz: %s, firmware_size: %s, is_support : %s',
            frequency_mhz, firmware_size, is_support)
        return REPLY_ERR
    end
    task_mgmt.update_task(task_id, {State = task_state.Running,
        Progress = process_bar_start_point + remain_process_bar * (1 / 3), Status = task_status.OK})
    local ok, ret = pcall(function ()
        local bios_ser = bios_factory.get_service('bios_service')
        -- multihost需要先切spi通道
        bios_ser:set_spi_channel_direction(sysid)

        self:download_bios(export_path, frequency_mhz, firmware_size)
        pcall(function ()
            if self:get_package_snapshot(SYSTEM_ID):get_period() ~= bios_enum.PackagePeriod.Period2 then
                log:notice('remove sensitive')
                bios_flash:remove_sensitive_info(export_path)
            end
        end)
    end)
    self:reset_driver()
    task_mgmt.update_task(task_id, {State = task_state.Running,
        Progress = process_bar_start_point + remain_process_bar * (2 / 3), Status = task_status.OK})
    if not ok then
        log:error('download bios %s firmware failed, msg: %s', sysid, ret)
        return REPLY_ERR
    end
    log:notice('download bios %s firmware successfully', sysid)
    return REPLY_OK
end
UpgradeService.mock_download_one_bios = download_one_bios

-- 开始下载bios固件
local function process_download_action(self, task_id, err_msg)
    log:notice('=========process download action=========')
    local size = bs_util.get_tbl_len(self.bios_object_collection)
    local response_code = REPLY_OK
    local current_idx   = 0
    local process_bar_start_point   = 5
    local ret
    for sysid, _ in pairs(self.bios_object_collection) do
        current_idx = current_idx + 1
        log:notice('start to download bios %s firmware, %s / %s', sysid, current_idx, size)
        ret = download_one_bios(self, sysid, task_id, process_bar_start_point, (1 / size) * 90)
        process_bar_start_point = process_bar_start_point + (1 / size) * 90
        if ret ~= REPLY_OK then
            err_msg(sysid)
            response_code = REPLY_ERR
        end
        task_mgmt.update_task(task_id, {State = task_state.Running,
            Progress = process_bar_start_point, Status = task_status.OK})
    end
    log:notice('=========process download action finished=========')
    return response_code
end

local function post_download_action(ctx)
    log:notice('=========post download action=========')
    log:notice('start to package bios firmware')
    utils.tar_zip(prop_def.DOWNLOAD_BIOS_POSITION, prop_def.EXPORT_DIR_NAME, prop_def.DOWNLOAD_BIOS_TAR_INSIDE_NAME)
    local uid, gid = utils_core.get_uid_gid_by_name(ctx.UserName)
    -- tar包权限设置为600
    utils_core.chmod_s(prop_def.DOWNLOAD_BIOS_TAR_INSIDE_NAME, utils.S_IRUSR | utils.S_IWUSR)
    client:PFileFileChown(context.new(), nil, prop_def.DOWNLOAD_BIOS_TAR_INSIDE_NAME, uid, gid)
    client:PFileFileMove(context.new(), nil, prop_def.DOWNLOAD_BIOS_TAR_INSIDE_NAME,
        prop_def.DOWNLOAD_BIOS_TAR_NAME, uid, gid)
    utils.remove_file(prop_def.EXPORT_BIOS_PATH)
    log:notice('=========post download action finished=========')
end

-- 适配单host与多host
function UpgradeService:download_multi_bios(task_id, ctx)
    local err_msg = error_msg()

    pre_download_action(self, task_id)

    local response_code = process_download_action(self, task_id, err_msg)

    post_download_action(ctx)
    if response_code ~= REPLY_OK then
        task_mgmt.update_task(task_id, {State = task_state.Exception, Status = task_status.Error,
            MessageId = 'BiosFirmwareExportFailed',
            MessageArgs = {err_msg()}})
        error(string.format('download bios firmware failed, system id : %s', err_msg()))
    end
    task_mgmt.update_task(task_id, {State = task_state.Completed,
            Progress = 100, Status = task_status.OK, MessageId = 'Success'})
end

function UpgradeService:download_bios(export_path, frequency_mhz, firm_ware_size)
    spi_flash.set_spi_owner(1) -- 切换总线到BMC
    spi_flash.insmod_driver(spi_def.SPIKODRV, frequency_mhz, prop_def.DEVNAME_BIOS_V6, client,
        client.PProxyDriverInsmod)  -- 安装驱动
    spi_flash.check_device_ready(prop_def.DEVNAME_BIOS_V6)
    client.PFileFileChown(client, context.new(), nil, prop_def.DEVNAME_BIOS_V6, 104, 104)

    -- 打开device
    local device_fd = mtd_api.open(prop_def.DEVNAME_BIOS_V6, prop_def.O_SYNC_RDWR)
    if device_fd == -1 then
        error('open mtd device failed')
    end

    local ok, ret = pcall(function ()
        local ret = bs_util.is_file_exist(export_path)
        if ret then
            utils.remove_file(export_path)
        end
        local file_fd = file_sec.open_s(export_path, 'a+')

        utils.safe_close_file(file_fd, function ()
            -- bin文件权限为440
            utils_core.chmod_s(export_path, utils.S_IRUSR | utils.S_IRGRP)

            spi_flash.write_data_from_flash(firm_ware_size, 0, file_fd, device_fd)
        end)
    end)
    if not ok then
        log:error('download_bios failed, ret = %s', ret)
    end
    mtd_api.close(device_fd)
end

function UpgradeService:reset_driver()
    pcall(function()
        spi_flash.rmmod_driver(spi_def.SPIKODRV, prop_def.DEVNAME_BIOS_V6, client, client.PProxyDriverRmmod)
    end)
    pcall(function()
        spi_flash.set_spi_owner(0)
    end)
end

function UpgradeService:ipmi_set_cached_bios_upgrade_mode(ctx, mode)
    if mode >= CACHED_BIOS_UPGRADE_MODE_MAX or mode < 0 then
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set the cached BIOS upgrade mode failed')
        return comp_code.ParmOutOfRange
    end

    local record = self.db:select(
        self.db.CachedBiosUpgradeTable
    ):where(self.db.CachedBiosUpgradeTable.Id:eq(1)):first()
    if record then
        record.CachedBiosUpgradeMode = mode
        record:save()
    else
        self.db.CachedBiosUpgradeTable({Id = 1, CachedBiosUpgradeMode = mode}):save()
    end
    ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set the cached BIOS upgrade mode to (0x%x) successfully', mode)
    return comp_code.Success
end

function UpgradeService:ipmi_get_cached_bios_upgrade_mode()
    local mode = self:get_cached_bios_upgrade_mode()
    return comp_code.Success, MANUFACTUREID, mode
end

function UpgradeService:get_cached_bios_upgrade_mode()
    local record = self.db:select(
        self.db.CachedBiosUpgradeTable
    ):where(self.db.CachedBiosUpgradeTable.Id:eq(1)):first()
    local mode = CACHED_BIOS_REBOOT_AND_POWER_OFF_UPGRADE
    if record then
        mode = record.CachedBiosUpgradeMode
    end

    return mode
end

function UpgradeService:check_soc_type(sysid)
    local frequency_mhz
    local firmware_size
    local is_support = true
    local snapshot = self:get_package_snapshot(SYSTEM_ID)
    local bios_ser = bios_factory.get_service('bios_service')
    if snapshot and snapshot:get_period() == bios_enum.PackagePeriod.Period2 then
        frequency_mhz = ''
        firmware_size = prop_def.BIOS_FILE_LEN
    else
        local mhz = self:get_spi_rate()
        log:notice('[bios]get spi rate is %s', mhz)
        frequency_mhz = string.format('g_clk_mhz=%s', mhz)
        local system_id = snapshot:get_system_id()
        local flash_size = bios_ser:get_prop(obj_def.FLASH_SIZE, system_id)
        firmware_size = flash_size * prop_def.BIOS_FILE_LEN_KUNPENGB
    end
    return frequency_mhz, firmware_size, is_support
end

function UpgradeService:import(set_value)
    local customize_upgrade = self.db:select(
        self.db.CachedBiosUpgradeTable
    ):where(self.db.CachedBiosUpgradeTable.Id:eq(1)):first()
    if not customize_upgrade then
        -- 初次导入时，环境可能不存在此表
        customize_upgrade = self.db.CachedBiosUpgradeTable({ Id = 1 })
    end
    if set_value ~= customize_upgrade.BiosActiveCondition then
        customize_upgrade.BiosActiveCondition = set_value
        log:operation(context:get_initiator(), 'BIOS',
            'Set BIOS active condition to %s successfully', set_value)
    end
    customize_upgrade:save()
    log:notice('Import(BMCSet_BIOS_UpgradeActiveCondition) successfully, value is %s', set_value)
end

function UpgradeService:export()
    local customize_upgrade = self.db:select(
        self.db.CachedBiosUpgradeTable
    ):where(self.db.CachedBiosUpgradeTable.Id:eq(1)):first()

    return customize_upgrade and customize_upgrade.BiosActiveCondition or 'PowerOff'
end

function UpgradeService:enable_multihost()
    self.multihost = true
end

function UpgradeService:config_spi_rate(object)
    self.spi_rate_obj = object
end

local PERIOD3_MHZ<const> = 15
function UpgradeService:get_spi_rate()
    if not self.spi_rate_obj or self.spi_rate_obj.Rate == 0 then
        return PERIOD3_MHZ
    end
    return self.spi_rate_obj.Rate
end

function UpgradeService:cache_hpms_after_activate()
    skynet.fork(function()
        local snapshots = self:get_package_snapshot_table(bios_enum.ALL_HOST_SYSTEM_ID)
        if not snapshots then
            return
        end
        local pfr_ser = pfr_service.get_instance()
        for _, snapshot in pairs(snapshots) do
            pfr_ser:cache_hpm_after_activate(snapshot)
        end
    end)
end

function UpgradeService:ipmi_set_bios_allowed_package_type(ctx, package_type)
    if package_type ~= ALLOWED_BIOS_SECURITY_UPGRADE and package_type ~= ALLOWED_BIOS_LOW_SECURITY_UPGRADE and
        package_type ~= PACKAGE_TYPE_UNDEFINED then
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set the bios allowed package type failed')
        return comp_code.ParmOutOfRange
    end
    local bios_package_info = get_the_bios_package_info(self.db)
    --仅装备版本支持设置
    if self.upgrade_mode ~= bios_enum.UpgradeMode.Force then
        log:error("This property has already been set and can not be set again")
        ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set the bios allowed package type failed')
        return comp_code.CommandDisabled
    else
        bios_package_info.AllowedPackageType = package_type
        bios_package_info.PackageStatus = BIOS_PACKAGE_TYPE_NOT_SET
        bios_package_info:save()
    end
    ipmi.ipmi_operation_log(ctx, 'BIOS', 'Set the bios allowed package type to (0x%x) successfully', package_type)
    return comp_code.Success
end

function UpgradeService:ipmi_get_bios_allowed_package_type()
    local package_status, package_type = self:get_bios_allowed_package_type()
    return comp_code.Success, package_status, package_type
end

function UpgradeService:get_bios_allowed_package_type()
    local bios_package_info = get_the_bios_package_info(self.db)
    return bios_package_info.PackageStatus, bios_package_info.AllowedPackageType
end

return singleton(UpgradeService)